// Copyright(c) 2016 Ruoyu Fan (Windy Darian), Xueyin Wan
// MIT License.
#ifndef _RENDERER_VULKAN_UTIL_H_
#define _RENDERER_VULKAN_UTIL_H_

#include "Raii.h"
#include <vulkan/vulkan.hpp>

#include <array>
#include <string>

class VContext;

/**
* a utility module for vulkan context
*/
class VUtility
{
public:

	VUtility(const VContext& context);
	~VUtility() = default;

	VUtility(VUtility&&) = delete;
	VUtility& operator= (VUtility&&) = delete;
	VUtility(const VUtility&) = delete;
	VUtility& operator= (const VUtility&) = delete;

	vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& available_formats);
	vk::PresentModeKHR chooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& available_present_modes);
	vk::Extent2D chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities);
	vk::Format findSupportedFormat(const std::vector<vk::Format>& candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features);
	inline vk::Format findDepthFormat()
	{
		return findSupportedFormat(
		{ vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint }
			, vk::ImageTiling::eOptimal
			, vk::FormatFeatureFlagBits::eDepthStencilAttachment
		);
	}

	std::tuple<VRaii<vk::Buffer>, VRaii<vk::DeviceMemory>> createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags property_bits, int sharing_queue_family_index_a = -1, int sharing_queue_family_index_b = -1);
	void copyBuffer(vk::Buffer src_buffer, vk::Buffer dst_buffer, vk::DeviceSize size, vk::DeviceSize src_offset = 0, vk::DeviceSize dst_offset = 0);

	std::tuple<VRaii<vk::Image>, VRaii<vk::DeviceMemory>> createImage(uint32_t image_width, uint32_t image_height
		, vk::Format format, vk::ImageTiling tiling
		, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags memory_properties);

	void copyImage(vk::Image src_image, vk::Image dst_image, uint32_t width, uint32_t height);
	void transitImageLayout(vk::Image image, vk::ImageLayout old_layout, vk::ImageLayout new_layout);

	VRaii<vk::ImageView> createImageView(vk::Image image, vk::Format format, vk::ImageAspectFlags aspect_mask);

	std::tuple<VRaii<vk::Image>, VRaii<vk::DeviceMemory>, VRaii<vk::ImageView>> loadImageFromFile(std::string path);

	VRaii<std::vector<vk::CommandBuffer>> createCommandBuffers(uint32_t count,vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary);

	vk::CommandBuffer beginSingleTimeCommands();
	void endSingleTimeCommands(vk::CommandBuffer commandBuffer);

	// Called on vulcan command buffer recording
	void recordCopyBuffer(vk::CommandBuffer command_buffer, vk::Buffer src_buffer, vk::Buffer dst_buffer, vk::DeviceSize size, vk::DeviceSize src_offset = 0, vk::DeviceSize dst_offset = 0);
	void recordCopyImage(vk::CommandBuffer command_buffer, vk::Image src_image, vk::Image dst_image, uint32_t width, uint32_t height);
	void recordTransitImageLayout(vk::CommandBuffer command_buffer, vk::Image image, vk::ImageLayout old_layout, vk::ImageLayout new_layout);

private:

	std::tuple<vk::Buffer, vk::DeviceMemory> createBufferImpl(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags property_bits, int sharing_queue_family_index_a = -1, int sharing_queue_family_index_b = -1);


	const VContext* context;
	vk::PhysicalDevice physical_device;
	vk::Device device;
	vk::Queue graphics_queue;
	vk::Queue compute_queue;
	vk::Queue present_queue;
	vk::CommandPool graphics_queue_command_pool;
	vk::CommandPool compute_queue_command_pool;

};

#endif